package logger

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"gitee.com/smewl13/toolkit"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type Log struct {
	Handlers  []string `mapstructure:"handlers"` // 日志输出方式
	Level     string   `mapstructure:"level"`    // 日志目录
	Path      string   `mapstructure:"dir"`      // 日志文件路径
	Retain    int      `mapstructure:"retain"`   // 文件最多保存多少天
	logWriter io.Writer
	errWriter io.Writer
	level     zapcore.Level
}

const (
	defaultFile = "running"
	defaultErr  = "error"
)

var (
	RootPath, ProcName = toolkit.GetRootPath()
	defaultPath        = filepath.Join(RootPath, "logs")

	// error logger
	errorLogger *zap.SugaredLogger

	levelMap = map[string]zapcore.Level{
		"debug":  zapcore.DebugLevel,
		"info":   zapcore.InfoLevel,
		"warn":   zapcore.WarnLevel,
		"error":  zapcore.ErrorLevel,
		"dpanic": zapcore.DPanicLevel,
		"panic":  zapcore.PanicLevel,
		"fatal":  zapcore.FatalLevel,
	}
)

func (l *Log) fileCore(encoder zapcore.Encoder) (cores []zapcore.Core) {
	l.setWriters()
	cores = append(
		cores,
		zapcore.NewCore(encoder, zapcore.AddSync(l.logWriter), zap.NewAtomicLevelAt(l.level)),
		zapcore.NewCore(
			encoder, zapcore.AddSync(l.errWriter), zap.NewAtomicLevelAt(zapcore.ErrorLevel),
		),
	)
	return
}

func (l *Log) setWriters() {
	if l.Path == "" {
		l.Path = defaultPath
	}

	if err := toolkit.MkdirAll(l.Path); err != nil {
		panic(err)
	}
	logFile := filepath.Join(l.Path, defaultFile)
	errFile := filepath.Join(l.Path, defaultErr)
	l.logWriter = l.getWriter(logFile)
	l.errWriter = l.getWriter(errFile)
}

// func (l *Log) getWriter(fileName string) zapcore.WriteSyncer {
// 	return zapcore.AddSync(&lumberjack.Logger{
// 		Filename:   fileName,
// 		MaxSize:    l.MaxSize,
// 		MaxBackups: l.MaxBackups,
// 		MaxAge:     l.MaxAge,
// 		LocalTime:  l.LocalTime,
// 		Compress:   l.Compress,
// 	})
// }

func (l *Log) getWriter(fileName string) io.Writer {
	// 生成rotatelogs的Logger 实际生成的文件名 demo.log.YYYY-mm-dd
	// demo.log是指向最新日志的链接
	// 保存7天内的日志，每1小时(整点)分割一次日志
	hook, err := rotatelogs.New(
		// 没有使用go风格反人类的format格式
		fmt.Sprintf("%s.%%Y-%%m-%%d.log", fileName),
		rotatelogs.WithLinkName(fmt.Sprintf("%s.log", fileName)),
		rotatelogs.WithMaxAge(time.Hour*24*time.Duration(l.Retain)),
		rotatelogs.WithRotationTime(time.Hour*24),
	)
	if err != nil {
		panic(err)
	}
	return hook
}

func (l *Log) getLoggerLevel() {
	var ok bool
	if l.level, ok = levelMap[l.Level]; ok {
		return
	}
	l.level = zapcore.InfoLevel
}

func (l *Log) setCores() (cores []zapcore.Core) {
	encoder := setEncoder()
	for _, handler := range l.Handlers {

		if handler == "console" {
			cores = append(
				cores,
				zapcore.NewCore(
					encoder,
					zapcore.AddSync(os.Stdout),
					zap.NewAtomicLevelAt(zapcore.DebugLevel),
				),
			)
		} else if handler == "file" {
			cores = append(cores, l.fileCore(encoder)...)
		}
	}
	if len(cores) == 0 {
		cores = append(cores, l.fileCore(encoder)...)
	}
	return
}

func (l *Log) Init() {
	l.getLoggerLevel()
	cores := zapcore.NewTee(l.setCores()...)
	logger := zap.New(cores, zap.AddCaller(), zap.AddCallerSkip(1))
	errorLogger = logger.Sugar()
}

func setEncoder() zapcore.Encoder {
	encoder := zap.NewProductionEncoderConfig()
	// encoder.EncodeTime = zapcore.ISO8601TimeEncoder
	encoder.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format("2006-01-02 15:04:05"))
	}
	encoder.EncodeLevel = zapcore.CapitalColorLevelEncoder
	return zapcore.NewConsoleEncoder(encoder)
}

func NewLog(log *Log) {
	log.Init()
}

func Debug(args ...interface{}) {
	errorLogger.Debug(args...)
}

func Debugf(template string, args ...interface{}) {
	errorLogger.Debugf(template, args...)
}

func Info(args ...interface{}) {
	errorLogger.Info(args...)
}

func Infof(template string, args ...interface{}) {
	errorLogger.Infof(template, args...)
}

func Warn(args ...interface{}) {
	errorLogger.Warn(args...)
}

func Warnf(template string, args ...interface{}) {
	errorLogger.Warnf(template, args...)
}

func Error(args ...interface{}) {
	errorLogger.Error(args...)
}

func Errorf(template string, args ...interface{}) {
	errorLogger.Errorf(template, args...)
}

func DPanic(args ...interface{}) {
	errorLogger.DPanic(args...)
}

func DPanicf(template string, args ...interface{}) {
	errorLogger.DPanicf(template, args...)
}

func Panic(args ...interface{}) {
	errorLogger.Panic(args...)
}

func Panicf(template string, args ...interface{}) {
	errorLogger.Panicf(template, args...)
}

func Fatal(args ...interface{}) {
	errorLogger.Fatal(args...)
}

func Fatalf(template string, args ...interface{}) {
	errorLogger.Fatalf(template, args...)
}
